#!/usr/bin/env python3

# check for the venv
from lib import sanity_check

sanity_check.check_venv(__file__)

import html
import os
import pprint
from collections import defaultdict
from typing import Any, Dict, List, Set

import orjson

Call = Dict[str, Any]


def clean_up_pattern(s: str) -> str:
    paren_level = 0
    in_braces = False
    result = ""
    prior_char = None
    for c in s:
        if c == "(":
            paren_level += 1
        if c == "<" and prior_char == "P":
            in_braces = True
        if in_braces or (paren_level == 0):
            if c != "?":
                result += c
        if c == ")":
            paren_level -= 1
        if c == ">":
            in_braces = False
        prior_char = c
    return result


def encode_info(info: Any) -> str:
    try:
        result = ""
        try:
            info = orjson.loads(info)
            result = "(stringified)\n"
        except orjson.JSONDecodeError:
            pass
        result += html.escape(pprint.pformat(info, indent=4))
        return "<pre>" + result + "</pre>"
    except Exception:
        pass
    try:
        return html.escape(str(info))
    except Exception:
        pass
    return "NOT ENCODABLE"


def fix_test_name(s: str) -> str:
    return s.replace("zerver.tests.", "")


def create_single_page(pattern: str, out_dir: str, href: str, calls: List[Call]) -> None:
    fn = out_dir + "/" + href
    with open(fn, "w") as f:
        f.write(
            """
            <style>
            .test {
                margin: 20px;
            }
            </style>
            """
        )
        f.write(f"<h3>{html.escape(pattern)}</h3>\n")
        calls.sort(key=lambda call: call["status_code"])
        for call in calls:
            f.write("<hr>")
            f.write("\n{}".format(fix_test_name(call["test_name"])))
            f.write('<div class="test">')
            f.write(call["url"])
            f.write("<br>\n")
            f.write(call["method"] + "<br>\n")
            f.write("status code: {}<br>\n".format(call["status_code"]))
            f.write("<br>")
            f.write("</div>")


def create_user_docs() -> None:
    fn = "var/url_coverage.txt"  # TODO: make path more robust, maybe use json suffix

    out_dir = "var/api_docs"
    try:
        os.mkdir(out_dir)
    except OSError:
        pass

    main_page = out_dir + "/index.html"

    with open(main_page, "w") as f:
        f.write(
            """
            <style>
            li {
                list-style-type: none;
            }

            a {
                text-decoration: none;
            }
            </style>
            """
        )

        with open(fn, "rb") as coverage:
            calls = [orjson.loads(line) for line in coverage]

        pattern_dict: Dict[str, List[Call]] = defaultdict(list)
        for call in calls:
            if "pattern" in call:
                pattern = clean_up_pattern(call["pattern"])
                if pattern:
                    pattern_dict[pattern].append(call)

        patterns = set(pattern_dict.keys())

        tups = [
            ("api/v1/external", "webhooks"),
            ("api/v1", "api"),
            ("json", "legacy"),
        ]

        groups: Dict[str, Set[str]] = {}
        for prefix, name in tups:
            groups[name] = {p for p in patterns if p.startswith(prefix)}
            patterns -= groups[name]

        groups["other"] = patterns

        for name in ["api", "legacy", "webhooks", "other"]:
            f.write(name + " endpoints:\n\n")
            f.write("<ul>\n")
            for pattern in sorted(groups[name]):
                href = pattern.replace("/", "-") + ".html"
                link = f'<a href="{href}">{html.escape(pattern)}</a>'
                f.write("<li>" + link + "</li>\n")
                create_single_page(pattern, out_dir, href, pattern_dict[pattern])
            f.write("</ul>")
            f.write("\n")

    print(f"open {main_page}")


if __name__ == "__main__":
    create_user_docs()
